home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Format CD 10
/
amigaformatcd10.iso
/
-screenplay-
/
shareware
/
mris 1.1
/
.mris1_1src.lha
/
draw.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-10-15
|
50KB
|
2,022 lines
/* Copyright (C) 1993, 1992 Nathan Sidwell */
/* RCS $Id: draw.c,v 4.17 1994/02/08 18:52:34 nathan Stable $ */
#include "xmris.h"
#ifndef AMIGAOS
#include "time.h"
#endif
/*{{{ board load error masks*/
#define ERROR_FILL 0x0001
#define ERROR_BACKGROUND 0x0002
#define ERROR_APPLES 0x0004
#define ERROR_APPLE_POSITION 0x0008
#define ERROR_PATH 0x0010
#define ERROR_CHAR 0x0020
#define ERROR_STRING 0x0040
#define ERROR_INCONSISTANT 0x0080
#define ERROR_NO_CHERRIES 0x0100
#define ERROR_TOO_FEW_APPLES 0x0200
#define ERROR_NO_DEN 0x0400
#define ERROR_NO_PLAYER 0x0800
#define ERROR_BAD_APPLES 0x1000
#define ERROR_SYNTAX 0x2000
#define ERROR_EOF 0x4000
#define ERROR_READING 0x8000
#define ERROR_MALLOC 0x10000
#define ERROR_OPEN 0x20000
#define ERROR_TOO_MANY 0x40000
#define ERROR_FIX_MASK 0x0DFF
#define ERROR_FATAL_MASK 0x7E000
#define ERROR_KEEP_MASK 0x7C000
/*}}}*/
#ifndef AMIGAOS
#define INCLUDE_LIMIT 16
#define FILE_SUFFIX ".gdn"
#define FILE_SUFFIX_LEN 4
/*{{{ typedef struct Include*/
typedef struct Include
{
char CONST *name; /* filename NULL for internal */
long seek; /* seek to next line */
unsigned error; /* error line */
unsigned line; /* line number */
unsigned start; /* start of garden line */
unsigned garden; /* garden number */
} INCLUDE;
/*}}}*/
/*{{{ parse*/
static struct
{
INCLUDE stack[INCLUDE_LIMIT]; /* include information */
unsigned depth; /* current include depth */
size_t limit; /* size of buffer */
char *text; /* line text */
} parse;
/*}}}*/
/*{{{ static BOARD internal_boards[] =*/
/*
* 0-9A-F selected apples on background.
* X fully connected path w/o cherry
* x fully connected path w cherry
* H-W explicitly connected path w/o cherry
* h-w explicitly connected path w cherry
* @ cherry on background
* + background denying random apple
* . background permitting random apple
*/
static BOARD internal_boards[] =
{
#include "digits.gdn"
};
/*}}}*/
/*{{{ static char CONST *text_error[] =*/
static char CONST *text_error[] =
{
"Bad fill pattern",
"Bad background colour",
"Too many apples",
"Bad apple position",
"Required paths missing",
"Bad character",
"String wrong length",
"Inconistant paths",
"No cherries",
"Too few apple locations",
"No den",
"No player",
"Inconsistant apples",
"Syntax error",
"Unexpected EOF",
"Error reading file",
"Cannot malloc",
"Cannot open file",
"Too many includes",
};
/*}}}*/
#else
#include "Catalog.h"
#endif /* !AMIGAOS */
/*{{{ prototypes*/
static VOIDFUNC draw_ball PROTOARG((unsigned));
static VOIDFUNC plot_ball PROTOARG((unsigned, int, int));
static VOIDFUNC new_board_draw PROTOARG((VOIDARG));
#ifndef AMIGAOS
static unsigned check_board PROTOARG((BOARD *));
static VOIDFUNC read_boards PROTOARG((char CONST *, char CONST *));
static unsigned read_line PROTOARG((FILE *, INCLUDE *));
#endif /* !AMIGAOS */
/*}}}*/
/*{{{ void add_background(x, y, w, h)*/
extern VOIDFUNC add_background
FUNCARG((x, y, width, height),
int x
ARGSEP int y
ARGSEP unsigned width
ARGSEP unsigned height
)
/*
* adds an area to the background update list
*/
{
BACKGROUND *bptr;
assert(update.back.backs != BACK_UPDATES);
bptr = &update.back.list[update.back.backs++];
bptr->place.x = x;
bptr->place.y = y;
bptr->size.x = width;
bptr->size.y = height;
return;
}
/*}}}*/
/*{{{ ANIMATE animate_zoom(next)*/
extern FUNCANIMATE(animate_zoom, next)
/*
* zooms out on the initial board
* and then return from whence I came
*/
{
static PROTOANIMATE((*rts));
static unsigned demo;
PROTOVOID(*then);
then = (PROTOVOID(*))animate_zoom;
if(next)
/*{{{ start*/
{
rts = (PROTOANIMATE((*)))next;
XFillRectangle(display.display, display.copy, GCN(GC_CLEAR),
BORDER_LEFT + 1, BORDER_TOP + 1, BOARD_WIDTH - 2, BOARD_HEIGHT - 2);
global.missed = 0;
refresh_window();
XCopyArea(display.display, display.back, display.copy, GCN(GC_COPY),
BORDER_LEFT + 1, BORDER_TOP + 1, BOARD_WIDTH - 2, BOARD_HEIGHT - 2,
BORDER_LEFT + 1, BORDER_TOP + 1);
/*{{{ do the apple sprites*/
{
int i;
APPLE *aptr;
for(aptr = apple.list, i = apple.apples; i--; aptr++)
{
SPRITE *sptr;
sptr = &sprites[SPRITE_APPLE];
XCopyArea(display.display, sptr->mask, display.copy, GCN(GC_MASK),
0, 0, CELL_WIDTH, CELL_HEIGHT, aptr->pixel.x, aptr->pixel.y);
XCopyArea(display.display, sptr->image, display.copy, GCN(GC_OR),
0, 0, CELL_WIDTH, CELL_HEIGHT, aptr->pixel.x, aptr->pixel.y);
}
}
/*}}}*/
demo = global.state == MODE_DEMO;
timer_set(ZOOM_RATE, TIMING_PAUSE);
global.count = 0;
global.state = MODE_ZOOM;
}
/*}}}*/
if(global.count >= BOARD_HEIGHT / (2 * ZOOM_Y) || global.throw == 1 ||
(demo && (global.pause
#ifndef AMIGAOS
|| global.pressed & (1 << KEY_QUIT | 1 << KEY_KEYBOARD)
#endif
)))
/*{{{ end*/
{
if(!demo && global.count < BOARD_HEIGHT / (2 * ZOOM_Y))
XCopyArea(display.display, display.copy, display.window,
GCN(GC_COPY), BORDER_LEFT + 1, BORDER_TOP + 1,
BOARD_WIDTH - 2, BOARD_HEIGHT - 2,
BORDER_LEFT + 1, BORDER_TOP + 1);
if(!demo && global.throw == 1)
global.throw = 2;
assert(rts != (PROTOANIMATE((*)))NULL);
then = (*rts)((PROTOVOID(*))NULL);
}
/*}}}*/
else
/*{{{ animate*/
{
unsigned x, y;
x = global.count * ZOOM_X;
y = global.count * ZOOM_Y;
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
CENTER_X - ZOOM_X - (int)x, CENTER_Y - ZOOM_Y - (int)y,
ZOOM_X, 2 * y + 2 * ZOOM_Y,
CENTER_X - ZOOM_X - (int)x, CENTER_Y - ZOOM_Y - (int)y);
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
CENTER_X + (int)x, CENTER_Y - ZOOM_Y - (int)y,
ZOOM_X, 2 * y + 2 * ZOOM_Y,
CENTER_X + (int)x, CENTER_Y - ZOOM_Y - (int)y);
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
CENTER_X - (int)x, CENTER_Y - ZOOM_Y - (int)y,
2 * x, ZOOM_Y,
CENTER_X - (int)x, CENTER_Y - ZOOM_Y - (int)y);
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
CENTER_X - (int)x, CENTER_Y + (int)y,
2 * x, ZOOM_Y,
CENTER_X - (int)x, CENTER_Y + (int)y);
global.count++;
}
/*}}}*/
return then;
}
/*}}}*/
static VOIDFUNC draw_blank_background
FUNCARG((map, cycle),
BOARD CONST *map
ARGSEP unsigned cycle
)
/*
* used to be part of blank_board()
*/
{
/*{{{ draw blank background*/
{
#ifndef AMIGAOS
XGCValues gcv;
#endif /* AMIGAOS */
unsigned back;
XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
back = (map->colors + cycle) % BACKGROUNDS;
if(data.mono == False)
color_set((int)back);
/*{{{ draw blank board*/
{
#ifndef AMIGAOS
gcv.fill_style = FillOpaqueStippled;
gcv.background = data.mono != False ? display.white :
colors[display.dynamic ? COLOR_DYNAMIC :
backgrounds[back][0]].pixel;
gcv.stipple = fills[map->fill].mask;
gcv.foreground = data.mono != False ?
display.black : colors[display.dynamic ?
COLOR_DYNAMIC + 1 : backgrounds[back][1]].pixel;
XChangeGC(display.display, GCN(GC_BOARD),
GCFillStyle | GCForeground | GCBackground | GCStipple, &gcv);
XFillRectangle(display.display, display.back, GCN(GC_BOARD),
BORDER_LEFT, BORDER_TOP, BOARD_WIDTH, BOARD_HEIGHT);
#else
FillBack(map->fill,back,BORDER_LEFT,BORDER_TOP,BOARD_WIDTH,BOARD_HEIGHT);
#endif /* AMIGAOS */
}
/*}}}*/
XDrawLine(display.display, display.back, GCN(GC_BORDER),
BORDER_LEFT, BORDER_TOP, BORDER_LEFT + BOARD_WIDTH, BORDER_TOP);
XDrawLine(display.display, display.back, GCN(GC_BORDER),
BORDER_LEFT, BORDER_TOP + BOARD_HEIGHT - 1,
BORDER_LEFT + BOARD_WIDTH, BORDER_TOP + BOARD_HEIGHT - 1);
XDrawLine(display.display, display.back, GCN(GC_BORDER),
PIXELX(4, -1), PIXELY(-1, 0), PIXELX(4, -1), PIXELY(-1, CELL_HEIGHT));
XDrawLine(display.display, display.back, GCN(GC_BORDER),
PIXELX(4, XTRA_SPACING * 4 + CELL_WIDTH), PIXELY(-1, 0),
PIXELX(4, XTRA_SPACING * 4 + CELL_WIDTH), PIXELY(-1, CELL_HEIGHT));
}
/*}}}*/
/*{{{ add the xtra*/
{
unsigned i;
extra.escape = 0;
for(i = 5; i--;)
draw_extra_letter(i);
}
/*}}}*/
}
/*{{{ void blank_board(map, cycle)*/
extern VOIDFUNC blank_board
FUNCARG((map, cycle),
BOARD CONST *map
ARGSEP unsigned cycle
)
/*
* sets up a blank board
*/
{
memset(garden, 0, sizeof(garden));
draw_blank_background(map,cycle);
/*{{{ initialize stuff*/
{
update.back.backs = 0;
update.score.scores = 0;
player.old_ball.state = 0;
player.old_ball.count = 8;
player.ball.state = 0;
player.ball.count = 8;
monster.monsters = 0;
apple.apples = 0;
}
/*}}}*/
return;
}
/*}}}*/
/*{{{ void bounding_box(x, y, width, height)*/
extern VOIDFUNC bounding_box
FUNCARG((x, y, width, height),
int x
ARGSEP int y
ARGSEP unsigned width
ARGSEP unsigned height
)
/*
* updates the update box so that it includes the
* supplied rectangle
* remember to empty the update box to the background list
*/
{
if(!update.set)
{
update.br.x = (update.tl.x = x) + width;
update.br.y = (update.tl.y = y) + height;
update.set = 1;
}
else
{
if(update.tl.x > x)
update.tl.x = x;
if(update.tl.y > y)
update.tl.y = y;
if(update.br.x < (int)(x + width))
update.br.x = (int)(x + width);
if(update.br.y < (int)(y + height))
update.br.y = (int)(y + height);
}
return;
}
/*}}}*/
/*{{{ unsigned check_board(map)*/
#ifndef AMIGAOS
static
#endif /* AMIGAOS */
unsigned check_board
FUNCARG((map),
BOARD *map
)
/* checks a board is ok
* and fix if possible
* returns a mask of the errors
*/
{
unsigned error;
error = 0;
/*{{{ check characters*/
{
char *cptr;
unsigned ix;
for(ix = CELLS_DOWN; ix--;)
for(cptr = map->map[ix]; *cptr; cptr++)
if(*cptr != GARDEN_RANDOM && *cptr != GARDEN_NOAPPLE &&
*cptr != GARDEN_CHERRY && !ISPATH(*cptr) && !ISAPPLE(*cptr))
{
error |= ERROR_CHAR;
*cptr = GARDEN_RANDOM;
}
}
/*}}}*/
/*{{{ check fill*/
if(map->fill >= FILLS)
{
error |= ERROR_FILL;
map->fill %= FILLS;
}
/*}}}*/
/*{{{ check background*/
if(map->colors >= BACKGROUNDS)
{
error |= ERROR_BACKGROUND;
map->colors %= BACKGROUNDS;
}
/*}}}*/
/*{{{ check apples number*/
if(map->apples > APPLE_LIMIT)
{
error |= ERROR_APPLES;
map->apples = APPLE_LIMIT;
}
/*}}}*/
/*{{{ check apple positioning*/
{
char *cptr;
for(cptr = map->map[CELLS_DOWN - 1]; *cptr; cptr++)
if(ISAPPLE(*cptr) || *cptr == GARDEN_RANDOM)
{
*cptr = GARDEN_NOAPPLE;
error |= ERROR_APPLE_POSITION;
}
}
/*}}}*/
/*{{{ check required paths*/
{
static CONST unsigned path[][2] =
{
{4, 0x2}, {5, 0x2}, {6, 0x2}, {7, 0x0},
};
unsigned ix;
for(ix = XtNumber(path); ix--;)
{
char *cptr;
unsigned c;
cptr = (char *)(map->map) + path[ix][0];
c = *cptr;
if(!ISPATH(c))
{
error |= ERROR_PATH;
*cptr = GARDEN_PATH + path[ix][1];
}
else if((GARDENPATH(c) & path[ix][1]) != path[ix][1])
{
error |= ERROR_PATH;
*cptr = (GARDENPATH(c) | path[ix][1]) + GARDEN_PATH;
}
}
}
/*}}}*/
/*{{{ check integrity*/
{
unsigned row;
char *cptr;
for(row = CELLS_DOWN; row--;)
for(cptr = map->map[row]; *cptr; cptr++)
/*{{{ check a cell*/
{
char c;
c = *cptr;
if(ISPATH(c))
{
unsigned paths;
paths = 0;
if(ISPATH(cptr[1]))
paths |= 2;
if(row != CELLS_DOWN - 1 &&
ISPATH(cptr[CELLS_ACROSS + 1]))
paths |= 1;
if((GARDENPATH(c) & paths) != (GARDENPATH(c) & 3))
{
error |= ERROR_INCONSISTANT;
*cptr = ((GARDENPATH(c) & 0xC) | paths) + GARDEN_PATH;
}
}
}
/*}}}*/
}
/*}}}*/
/*{{{ check totals*/
{
char *cptr;
unsigned count;
unsigned totals[8];
for(count = 8; count--;)
totals[count] = 0;
for(count = (CELLS_ACROSS + 1) * CELLS_DOWN,
cptr = (char *)map->map; count--; cptr++)
if(ISPATHCHERRY(*cptr) || *cptr == GARDEN_CHERRY)
totals[4]++;
else if(ISPATHDEN(*cptr))
totals[5]++;
else if(ISPATHPLAYER(*cptr))
totals[6]++;
else if(ISAPPLE(*cptr))
{
unsigned value;
unsigned ix;
value = GARDENAPPLE(*cptr);
for(ix = 4; ix--; value >>= 1)
if(value & 1)
totals[ix]++;
totals[7]++;
}
else if(*cptr == GARDEN_RANDOM)
totals[7]++;
do
{
char c;
cptr = NULL;
c = 0;
if(!totals[5])
{
error |= ERROR_NO_DEN;
cptr = &map->map[DEFAULT_DEN_Y][DEFAULT_DEN_X];
c = GARDEN_PATH_DEN;
}
if(!totals[6])
{
error |= ERROR_NO_PLAYER;
cptr = &map->map[DEFAULT_PLAYER_Y][DEFAULT_PLAYER_X];
c = GARDEN_PATH_PLAYER;
}
if(!totals[4])
{
error |= ERROR_NO_CHERRIES;
cptr = &map->map[DEFAULT_CHERRY_Y][DEFAULT_CHERRY_X];
c = GARDEN_PATH_CHERRY;
}
if(cptr)
{
if(*cptr == GARDEN_CHERRY || ISPATHCHERRY(*cptr))
totals[4]--;
if(ISPATH(*cptr))
c = (GARDENPATH(*cptr) & 3) + c + GARDEN_PATH;
else if(c == GARDEN_PATH_CHERRY)
c = GARDEN_CHERRY;
else
c += GARDEN_PATH;
*cptr = c;
}
if(totals[7] < map->apples)
error |= ERROR_TOO_FEW_APPLES;
}
while(cptr);
for(count = 4; count--;)
if(totals[count] != map->apples)
error |= ERROR_BAD_APPLES;
}
/*}}}*/
#ifdef AMIGAOS
error&=ERROR_KEEP_MASK;
#endif /* AMIGAOS */
return error;
}
/*}}}*/
#ifndef AMIGAOS
/*{{{ void create_boards()*/
extern VOIDFUNC create_boards FUNCARGVOID
/* initializes the boards
* either from the internal ones, or
* by reading the user specified file
*/
{
read_boards(data.boards, NULL);
if(!board.boards)
{
fputs("No gardens found, using default\n", stderr);
parse.depth = 0;
read_boards(NULL, NULL);
}
if(parse.text)
free(parse.text);
return;
}
/*}}}*/
#endif /* !AMIGAOS */
/*{{{ void create_xtra_monster(ix)*/
extern VOIDFUNC create_xtra_monster
FUNCARG((ix),
unsigned ix
)
/*
* create an xtra monster sprite, by taking the virgin one, and
* slapping a letter on its chest
*/
{
SPRITE *dptr;
SPRITE *sptr;
SPRITE *lptr;
unsigned i;
sptr = &sprites[SPRITE_XTRA];
lptr = &sprites[SPRITE_EXTRA];
for(dptr = &sprites[SPRITE_XTRA], sptr = &sprites[SPRITE_XTRA_SOURCE],
i = MONSTER_IMAGES; i--; dptr++, sptr++)
{
XCopyArea(display.display, sptr->image, dptr->image, GCN(GC_COPY),
0, 0, CELL_WIDTH, CELL_HEIGHT, 0, 0);
XCopyArea(display.display, lptr->mask, dptr->image, GCN(GC_MASK),
(int)ix * (CELL_WIDTH / 2), 0, CELL_WIDTH / 2, CELL_HEIGHT / 2,
XTRA_LETTER_X, XTRA_LETTER_Y);
XCopyArea(display.display, lptr[!(extra.got & 1 << ix)].image,
dptr->image, GCN(GC_OR),
(int)ix * (CELL_WIDTH / 2), 0, CELL_WIDTH / 2, CELL_HEIGHT / 2,
XTRA_LETTER_X, XTRA_LETTER_Y);
}
return;
}
/*}}}*/
/*{{{ void draw_ball(flag)*/
static VOIDFUNC draw_ball
FUNCARG((flag),
unsigned flag
)
/*
* draws the old ball on or off the window
*/
{
switch(player.old_ball.state)
{
/*{{{ case 0:*/
case 0:
{
if(player.old_ball.count < 8)
{
COORD CONST *hold;
hold = &ball_hold[player.old_ball.count * MONSTER_IMAGES +
player.old_ball.image];
plot_ball(flag, player.old_ball.pixel.x + hold->x,
player.old_ball.pixel.y + hold->y);
}
break;
}
/*}}}*/
/*{{{ case 1:*/
case 1:
{
plot_ball(flag, player.old_ball.pixel.x, player.old_ball.pixel.y);
break;
}
/*}}}*/
/*{{{ case 2: case 4:*/
case 2: case 4:
{
COORD offset;
COORD pixel;
unsigned bits;
pixel.x = player.old_ball.pixel.x;
pixel.y = player.old_ball.pixel.y;
if(player.old_ball.count)
{
offset.x = player.old_ball.count * BALL_EX;
offset.y = player.old_ball.count * BALL_EY;
bits = 0xFF;
/*{{{ set clips*/
{
if(pixel.x < offset.x)
bits &= 0xF8;
if(pixel.y < offset.y)
bits &= 0x3E;
if(pixel.x + offset.x > BOARD_WIDTH + BALL_WIDTH)
bits &= 0x8F;
if(pixel.y + offset.y > BOARD_HEIGHT + BALL_HEIGHT)
bits &= 0xE3;
}
/*}}}*/
/*{{{ do inner bits*/
{
if(bits & 0x01)
plot_ball(flag, pixel.x - offset.x, pixel.y - offset.y);
if(bits & 0x02)
plot_ball(flag, pixel.x - offset.x, pixel.y);
if(bits & 0x04)
plot_ball(flag, pixel.x - offset.x, pixel.y + offset.y);
if(bits & 0x08)
plot_ball(flag, pixel.x, pixel.y + offset.y);
if(bits & 0x10)
plot_ball(flag, pixel.x + offset.x, pixel.y + offset.y);
if(bits & 0x20)
plot_ball(flag, pixel.x + offset.x, pixel.y);
if(bits & 0x40)
plot_ball(flag, pixel.x + offset.x, pixel.y - offset.y);
if(bits & 0x80)
plot_ball(flag, pixel.x, pixel.y - offset.y);
}
/*}}}*/
offset.x *= 2;
offset.y *= 2;
/*{{{ set clips*/
{
if(pixel.x < offset.x)
bits &= 0xF8;
if(pixel.y < offset.y)
bits &= 0x3E;
if(pixel.x + offset.x > BOARD_WIDTH + BALL_WIDTH)
bits &= 0x8F;
if(pixel.y + offset.y > BOARD_HEIGHT + BALL_HEIGHT)
bits &= 0xE3;
}
/*}}}*/
/*{{{ do inner bits*/
{
if(bits & 0x01)
plot_ball(flag, pixel.x - offset.x, pixel.y - offset.y);
if(bits & 0x02)
plot_ball(flag, pixel.x - offset.x, pixel.y);
if(bits & 0x04)
plot_ball(flag, pixel.x - offset.x, pixel.y + offset.y);
if(bits & 0x08)
plot_ball(flag, pixel.x, pixel.y + offset.y);
if(bits & 0x10)
plot_ball(flag, pixel.x + offset.x, pixel.y + offset.y);
if(bits & 0x20)
plot_ball(flag, pixel.x + offset.x, pixel.y);
if(bits & 0x40)
plot_ball(flag, pixel.x + offset.x, pixel.y - offset.y);
if(bits & 0x80)
plot_ball(flag, pixel.x, pixel.y - offset.y);
}
/*}}}*/
}
else
plot_ball(flag, pixel.x, pixel.y);
break;
}
/*}}}*/
/*{{{ case 3:*/
case 3:
break;
/*}}}*/
}
return;
}
/*}}}*/
/*{{{ void draw_extra()*/
extern VOIDFUNC draw_extra FUNCARGVOID
/*
* whack the current extra monster onto the background
*/
{
int x;
x = PIXELX(4, extra.select * XTRA_SPACING);
back_sprite(SPRITE_XTRA + (chaotic() & 1), 1, x, PIXELY(-1, 0));
add_background(x, PIXELY(-1, 0), CELL_WIDTH, CELL_HEIGHT);
return;
}
/*}}}*/
/*{{{ void draw_extra_letter(ix)*/
extern VOIDFUNC draw_extra_letter
FUNCARG((ix),
unsigned ix
)
/*
* draw one of the extra letters on the background
* and add it to the update list
*/
{
SPRITE *lptr;
int x;
lptr = &sprites[SPRITE_EXTRA];
x = PIXELX(4, ix * XTRA_SPACING);
XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
x, PIXELY(-1, 0), CELL_WIDTH, CELL_HEIGHT);
if(display.background != COLOUR_ZERO)
XCopyArea(display.display, lptr->mask,
display.back, GCN(GC_MASK), (int)ix * (CELL_WIDTH / 2), 0,
CELL_WIDTH / 2, CELL_HEIGHT / 2,
x + XTRA_LETTER_X, PIXELY(-1, XTRA_LETTER_Y));
XCopyArea(display.display, lptr[!(extra.got & 1 << ix)].image,
display.back, GCN(GC_OR), (int)ix * (CELL_WIDTH / 2), 0,
CELL_WIDTH / 2, CELL_HEIGHT / 2,
x + XTRA_LETTER_X, PIXELY(-1, XTRA_LETTER_Y));
add_background(x, PIXELY(-1, 0), CELL_WIDTH, CELL_HEIGHT);
return;
}
/*}}}*/
static VOIDFUNC new_board_draw FUNCARGVOID
/*
* used to be part of new_board()
*/
{
XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
BORDER_LEFT + (CELL_WIDTH + GAP_WIDTH) * 4 + GAP_WIDTH, BORDER_TOP + 1,
XTRA_SPACING * 4 + CELL_WIDTH, GAP_HEIGHT * 2 - 1);
draw_extra();
/*{{{ add the screen number*/
{
#ifndef AMIGAOS
static char text[] = "Garden 000";
int length;
length = 7 + itoa(text + 7, (unsigned long)global.screen, 0);
XDrawImageString(display.display, display.back, GCN(GC_TEXT),
PIXELX(8, (int)font.width * 2),
PIXELY(-1, CELL_HEIGHT / 2 + font.center), text, length);
#else
struct Rectangle Rectangle;
struct RastPort *RastPort;
RastPort=display.back.Layer->rp;
Rectangle.MinX=PIXELX(8,0);
Rectangle.MaxX=PIXELX(CELLS_ACROSS,-GAP_WIDTH);
Rectangle.MinY=PIXELY(-1,CELL_HEIGHT/2-RastPort->TxHeight/2);
Rectangle.MaxY=Rectangle.MinY+RastPort->TxHeight;
if (InstallBounds(&display.back,Rectangle))
{
const char *Template;
Template=GetString(MSG_GARDEN_NUMBER);
SetABPenDrMd(RastPort,display.black,display.black,JAM1);
Move(RastPort,Rectangle.MaxX-GS_StringWidth(Template,&global.screen,RastPort,Locale),
Rectangle.MinY+RastPort->TxBaseline);
GS_DrawString(Template,&global.screen,RastPort,Locale);
RemoveBounds(&display.back);
}
#endif /* AMIGAOS */
}
/*}}}*/
add_score(0, 0, 0);
/*{{{ add lives*/
if(global.lives)
{
unsigned lives;
for(lives = global.lives - 1; lives--;)
back_sprite(SPRITE_PLAYER + 6, 0,
PIXELX((int)lives, 0), PIXELY(CELLS_DOWN, 0));
}
/*}}}*/
/*{{{ cut the background*/
{
unsigned y, x;
COORD cell;
CELL *cptr;
cptr = BOARDCELL(0, 0);
cell.x = PIXELX(0, 0);
cell.y = PIXELY(0, 0);
for(y = CELLS_DOWN; y--; cptr += CELL_STRIDE - CELLS_ACROSS,
cell.x -= (CELL_WIDTH + GAP_WIDTH) * CELLS_ACROSS,
cell.y += CELL_HEIGHT + GAP_HEIGHT)
for(x = CELLS_ACROSS; x--; cptr++, cell.x += CELL_WIDTH + GAP_WIDTH)
{
if(cptr->visit)
munch_hole(cptr, cell.x, cell.y);
if(cptr->sprite)
back_sprite(cptr->sprite, 1, cell.x, cell.y);
if(cptr->den)
XFillRectangle(display.display, display.back,
GCN(GC_CLEAR), cell.x - DEN_WIDTH,
cell.y - DEN_HEIGHT,
CELL_WIDTH + DEN_WIDTH * 2, CELL_HEIGHT + DEN_HEIGHT * 2);
}
}
/*}}}*/
XCopyArea(display.display, display.back, display.copy, GCN(GC_COPY),
0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
}
/*{{{ void new_board()*/
extern VOIDFUNC new_board FUNCARGVOID
/*
* sets up a new board
*/
{
BOARD CONST *map;
int spaces;
#ifndef AMIGAOS
map = board.list[(global.screen - 1) % board.boards];
#else
map = &board.list[(global.screen - 1) % board.boards];
#endif /* AMIGAOS */
blank_board(map, (global.screen - 1) / board.boards);
apple.apples = 0;
spaces = 0;
global.cherries = 0;
global.dens = 0;
/*{{{ copy the board*/
{
unsigned mask;
mask = 1 << (chaotic() % 4);
/*{{{ copy the text map*/
{
CELL *cptr;
char CONST *mptr;
unsigned y, x;
mptr = (char CONST *)map->map;
for(y = 0, cptr = BOARDCELL(0, 0);
y != CELLS_DOWN; y++, cptr += CELL_STRIDE - CELLS_ACROSS, mptr++)
for(x = 0; x != CELLS_ACROSS; x++, mptr++, cptr++)
{
char c;
c = *mptr;
/*{{{ cherry?*/
if(c == GARDEN_CHERRY || ISPATHCHERRY(c))
{
cptr->sprite = SPRITE_CHERRY;
global.cherries++;
}
/*}}}*/
if(ISAPPLE(c))
/*{{{ explicit apple*/
{
cptr->xtra = data.random != False || !(mask & GARDENAPPLE(c));
if(!cptr->xtra && apple.apples < APPLE_LIMIT)
spawn_apple(x, y, 0, 0);
}
/*}}}*/
else if(ISPATH(c))
/*{{{ explicitly connected path*/
{
unsigned bits;
bits = GARDENPATH(c) & 3;
cptr->visit = 1;
if(bits & 1)
{
cptr[0].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
}
if(bits & 2)
{
cptr[0].depths[3] = CELL_WIDTH + GAP_WIDTH;
cptr[1].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
}
if(ISPATHDEN(c))
{
cptr->den = 1;
global.dens++;
}
else if(ISPATHPLAYER(c))
{
global.start.x = x;
global.start.y = y;
}
}
/*}}}*/
else if(c == GARDEN_RANDOM)
cptr->xtra = 1;
spaces += cptr->xtra;
}
}
/*}}}*/
}
/*}}}*/
if(apple.apples < map->apples)
/*{{{ add the apples*/
{
unsigned count;
for(count = map->apples - apple.apples; spaces && count--;)
{
CELL *cptr;
/*{{{ select a location*/
{
unsigned count;
do
count = chaotic();
while(count >= 256 / spaces * spaces);
count %= spaces;
#ifndef AMIGAOS
for(cptr = BOARDCELL(0, 0); !cptr->xtra || !count--; cptr++)
/* EMPTY */;
#else
for(cptr = BOARDCELL(0, 0); !cptr->xtra || count--; cptr++)
/* EMPTY */;
#endif
}
/*}}}*/
cptr->apple = 1;
cptr->xtra = 0;
spaces--;
/*{{{ clear surround*/
{
static CONST int offsets[] = {-1, 1, CELL_STRIDE, -CELL_STRIDE};
unsigned ix;
for(ix = sizeof(offsets) / sizeof(offsets[0]); ix--;)
{
CELL *optr;
optr = cptr + offsets[ix];
spaces -= (int)optr->xtra;
optr->ball |= optr->xtra;
optr->xtra = 0;
}
}
/*}}}*/
/*{{{ run out of spaces?*/
if(spaces <= 0 && count)
{
unsigned count;
#ifdef AMIGAOS
CELL *cptr;
#endif
for(count = CELLS_DOWN * CELL_STRIDE, cptr = BOARDCELL(0, 0);
count--; cptr++)
if(cptr->ball)
{
cptr->ball = 0;
cptr->xtra = 1;
spaces++;
}
}
/*}}}*/
spawn_apple((cptr - BOARDCELL(0, 0)) % CELL_STRIDE,
(cptr - BOARDCELL(0, 0)) / CELL_STRIDE, 0, 0);
}
}
/*}}}*/
new_board_draw();
}
/*}}}*/
/*{{{ void plot_ball(flag, x, y)*/
static VOIDFUNC plot_ball
FUNCARG((flag, x, y),
unsigned flag
ARGSEP int x
ARGSEP int y
)
/*
* plots the ball centered on the given coordinate
* flag is 0 for unplot, 1 for plot
*/
{
if(flag)
{
XCopyArea(display.display, sprites[SPRITE_BALL].mask, display.copy,
GCN(GC_MASK), 0, 0, BALL_WIDTH, BALL_HEIGHT,
x - BALL_WIDTH / 2, y - BALL_HEIGHT / 2);
XCopyArea(display.display, sprites[SPRITE_BALL].image, display.copy,
GCN(GC_OR), 0, 0, BALL_WIDTH, BALL_HEIGHT,
x - BALL_WIDTH / 2, y - BALL_HEIGHT / 2);
}
add_background(x - BALL_WIDTH / 2, y - BALL_HEIGHT / 2,
BALL_WIDTH, BALL_HEIGHT);
return;
}
/*}}}*/
#ifndef AMIGAOS
/*{{{ void read_boards()*/
static VOIDFUNC read_boards
FUNCARG((name, parent),
char CONST *name
ARGSEP char CONST *parent
)
/* read a board file
* recurses to deal with include
*/
{
INCLUDE *sptr;
FILE *stream;
unsigned error;
BOARD *bptr;
sptr = &parse.stack[parse.depth];
sptr->name = NULL;
error = 0;
sptr->line = 0;
sptr->start = 0;
sptr->garden = 0;
stream = NULL;
/*{{{ open?*/
if(name)
{
char *filename;
size_t length;
length = strlen(name);
if(*name == '/')
{
filename = XtMalloc(length + 1 + FILE_SUFFIX_LEN);
strcpy(filename, name);
strcpy(&filename[length], FILE_SUFFIX);
if(filename)
{
stream = fopen(filename, "r");
if(!stream)
{
filename[length] = 0;
stream = fopen(filename, "r");
}
}
else
error = ERROR_MALLOC;
}
else
{
size_t offset;
filename = parent ? strrchr(parent, '/') : NULL;
offset = filename ? filename - parent + 1 : 0;
filename = XtMalloc(length + offset + 1 + FILE_SUFFIX_LEN);
strncpy(filename, parent, offset);
strcpy(&filename[offset], name);
strcpy(&filename[offset + length], FILE_SUFFIX);
stream = fopen(filename, "r");
if(!stream)
{
filename[offset + length] = 0;
stream = fopen(filename, "r");
}
if(!stream)
/*{{{ try in app-defaults/xmris/<file>*/
{
char *lib;
lib = XtResolvePathname(display.display,
"app-defaults/xmris", name, NULL, NULL, NULL, 0, NULL);
if(!lib)
lib = XtResolvePathname(display.display, "app-defaults/xmris",
name, FILE_SUFFIX, NULL, NULL, 0, NULL);
if(!lib && data.dir)
lib = XtResolvePathname(display.display, data.dir, name,
FILE_SUFFIX, "%T/%N:%T/%N%S:%T/gardens/%N:%T/gardens/%N%S",
NULL, 0, NULL);
if(lib)
{
XtFree(filename);
filename = lib;
}
stream = fopen(filename, "r");
}
/*}}}*/
}
sptr->name = filename;
if(!stream && filename)
{
perror(filename);
error = ERROR_OPEN;
}
}
else
stream = 0;
/*}}}*/
do
/*{{{ read a board*/
{
bptr = NULL;
if(error)
/* EMPTY */;
else if(sptr->name)
/*{{{ read board*/
{
/* state 0 new block
* state 1 comment
* state 2-4 numbers
* state 5 open brace
* state 6 close brace
* state 7 close brace
* state 10+ lines
*/
unsigned state;
state = 0;
sptr->start = sptr->line + 1;
do
{
char *ptr;
/*{{{ read line*/
{
unsigned temp;
temp = read_line(stream, sptr);
if(!error)
sptr->error = sptr->line;
if(temp)
{
error |= temp;
break;
}
}
/*}}}*/
ptr = parse.text;
if(!*ptr)
{
if(state)
error |= ERROR_EOF;
break;
}
while(*ptr == ' ' || *ptr == '\t')
ptr++;
if(*ptr == '\n')
continue;
/*{{{ new block*/
if(state == 0)
{
if(ptr[0] == '/' && ptr[1] == '*')
{
ptr += 2;
state = 1;
}
else if(!strncmp(ptr, "#include", 8))
/*{{{ include*/
{
ptr += 8;
while(*ptr == ' ' || *ptr == '\t')
ptr++;
if(*ptr == '\"')
{
char *name;
name = ++ptr;
while(*ptr && *ptr != '\"')
ptr++;
*ptr = 0;
if(ptr == name)
name = NULL;
if(parse.depth == INCLUDE_LIMIT)
{
error = ERROR_TOO_MANY;
break;
}
sptr->seek = ftell(stream);
fclose(stream);
parse.depth++;
read_boards(name, sptr->name);
parse.depth--;
stream = fopen(sptr->name, "r");
if(!stream || fseek(stream, sptr->seek, SEEK_SET))
{
error = ERROR_OPEN;
perror(sptr->name);
break;
}
}
else
{
error = ERROR_SYNTAX;
break;
}
}
/*}}}*/
else if(*ptr == '{')
{
ptr++;
state = 2;
bptr = malloc(sizeof(BOARD));
if(!bptr)
{
error = ERROR_MALLOC;
state = 7;
}
}
else
error = ERROR_SYNTAX;
}
/*}}}*/
/*{{{ in comment*/
if(state == 1)
{
while(*ptr && state)
{
while(*ptr && *ptr != '*')
ptr++;
if(*ptr)
{
if(ptr[1] == '/')
{
ptr += 2;
state = 0;
break;
}
else
ptr++;
}
}
}
/*}}}*/
/*{{{ reading board*/
if(state >= 2)
{
while(state)
{
if(*ptr == ' ' || *ptr == '\t')
ptr++;
if(*ptr == '\n')
break;
switch(state)
{
/*{{{ case 2: case 3: case 4:*/
case 2: case 3: case 4:
{
char *eptr;
unsigned value;
value = strtol(ptr, &eptr, 0);
if(eptr == ptr)
{
error = ERROR_SYNTAX;
state = 7;
}
else
{
switch(state)
{
case 2:
bptr->fill = value;
/* CASCADE */
case 3:
bptr->colors = value;
/* CASCADE */
case 4:
bptr->apples = value;
/* CASCADE */
}
ptr = eptr;
state++;
}
break;
}
/*}}}*/
/*{{{ case 5:*/
case 5:
if(*ptr == '{')
{
state = 10;
ptr++;
}
else
{
state = 7;
error = ERROR_SYNTAX;
}
break;
/*}}}*/
/*{{{ case 6:*/
case 6:
if(*ptr++ == '}')
state = 7;
else
error = ERROR_SYNTAX;
break;
/*}}}*/
/*{{{ case 7:*/
case 7:
if(*ptr == '}')
{
state = 0;
ptr++;
}
else if(*ptr++ == '{')
state = 6;
else
error = ERROR_SYNTAX;
break;
/*}}}*/
/*{{{ default:*/
default:
{
char *eptr;
assert(state >= 10 && state < 10 + CELLS_DOWN);
eptr = strchr(ptr + 1, '\"');
if(*ptr != '\"' || !eptr ||
eptr - ptr != CELLS_ACROSS + 1)
{
error = ERROR_SYNTAX;
state = 6;
}
else
{
strncpy(bptr->map[state - 10], ptr + 1,
CELLS_ACROSS);
bptr->map[state - 10][CELLS_ACROSS] = 0;
state++;
ptr = eptr + 1;
if(state == 10 + CELLS_DOWN)
state = 6;
}
break;
}
/*}}}*/
}
if(error && bptr)
{
free(bptr);
bptr = NULL;
}
if(*ptr == ',')
ptr++;
}
}
/*}}}*/
}
while(state);
if(!error && !*parse.text)
break;
}
/*}}}*/
else if(sptr->garden != sizeof(internal_boards) /
sizeof(internal_boards[0]))
bptr = &internal_boards[sptr->garden];
else
break;
/*{{{ a new garden?*/
if(bptr)
{
if(!(error & ERROR_FATAL_MASK))
error |= check_board(bptr);
if(board.boards == BOARD_LIMIT)
error |= ERROR_TOO_MANY;
if(!(error & ERROR_FATAL_MASK))
board.list[board.boards++] = bptr;
else
{
if(sptr->name)
free(bptr);
bptr = NULL;
}
}
/*}}}*/
/*{{{ error*/
if(error)
{
unsigned depth;
int ix;
fprintf(stderr, "In file %s", sptr->name ? sptr->name :
"<default>");
for(depth = parse.depth; depth--;)
fprintf(stderr, " included from %s:%d",
parse.stack[depth].name, parse.stack[depth].line);
fprintf(stderr, " line %d", sptr->error);
if(sptr->start != sptr->line)
fprintf(stderr, " garden %d (lines %d-%d)",
sptr->garden + 1, sptr->start, sptr->line);
fputc('\n', stderr);
for(ix = 16; ix--;)
if(error & 1 << ix)
fprintf(stderr, "%s%s\n", text_error[ix],
(1 << ix) & ERROR_FIX_MASK ? " (fixed)" : "");
error &= ERROR_KEEP_MASK;
}
/*}}}*/
if(bptr)
sptr->garden++;
}
/*}}}*/
while(!error);
if(stream)
fclose(stream);
XtFree((char *)sptr->name);
return;
}
/*}}}*/
/*{{{ unsigned read_line(stream)*/
static unsigned read_line
FUNCARG((stream, sptr),
FILE *stream
ARGSEP INCLUDE *sptr
)
{
size_t length;
char *ptr;
unsigned error;
char *line;
sptr->line++;
error = 0;
if(!parse.text)
{
parse.limit = 128;
parse.text = malloc(128);
if(!parse.text)
return ERROR_MALLOC;
}
length = 0;
ptr = parse.text;
/*{{{ read line*/
while((line = fgets(ptr, parse.limit - length, stream)))
{
ptr += strlen(ptr);
length = ptr - parse.text;
if(length && *(ptr - 1) == '\n')
break;
if(length + 1 < parse.limit)
break;
ptr = realloc(parse.text, parse.limit + 128);
if(ptr)
{
parse.limit += 128;
parse.text = ptr;
ptr += length;
}
else
{
error = ERROR_MALLOC;
break;
}
}
/*}}}*/
/*{{{ error?*/
if(!line)
{
if(feof(stream))
*parse.text = 0;
else
{
perror(sptr->name);
error = ERROR_READING;
}
}
/*}}}*/
return error;
}
/*}}}*/
#endif /* !AMIGAOS */
/*{{{ void refresh_window()*/
extern VOIDFUNC refresh_window FUNCARGVOID
/*
* refreshes the display window
*/
{
if(global.state == MODE_ZOOM)
{
unsigned x, y;
x = global.count * ZOOM_X;
y = global.count * ZOOM_Y;
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
0, 0, WINDOW_WIDTH, BORDER_TOP + 1, 0, 0);
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
0, BORDER_TOP + BOARD_HEIGHT - 1, WINDOW_WIDTH, BORDER_BOTTOM + 1,
0, BORDER_TOP + BOARD_HEIGHT - 1);
if(BORDER_LEFT > 0)
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
0, BORDER_TOP, BORDER_LEFT + 1, BOARD_HEIGHT, 0, BORDER_TOP);
if(BORDER_RIGHT > 0)
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
BORDER_LEFT + BOARD_WIDTH - 1, BORDER_TOP, BORDER_RIGHT + 1,
BOARD_HEIGHT, BORDER_LEFT + BOARD_WIDTH - 1, BORDER_TOP);
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
CENTER_X - (int)x, CENTER_Y - (int)y, 2 * x, 2 * y,
CENTER_X - (int)x, CENTER_Y - (int)y);
}
else
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
if(global.missed)
XDrawLine(display.display, display.window, GCN(GC_LOAD),
WINDOW_WIDTH - (int)global.missed,
PIXELY(CELLS_DOWN, CELL_HEIGHT),
WINDOW_WIDTH, PIXELY(CELLS_DOWN, CELL_HEIGHT));
if(global.dilation != FRAME_SCALE)
XDrawLine(display.display, display.window, GCN(GC_LOAD),
0, PIXELY(CELLS_DOWN, CELL_HEIGHT),
WINDOW_WIDTH - (int)((unsigned long)WINDOW_WIDTH * FRAME_SCALE /
global.dilation), PIXELY(CELLS_DOWN, CELL_HEIGHT));
return;
}
/*}}}*/
/*{{{ void set_back_sprite(ix)*/
extern VOIDFUNC set_back_sprite
FUNCARG((ix, cell_x, cell_y),
unsigned ix
ARGSEP unsigned cell_x
ARGSEP unsigned cell_y
)
/*
* sets a den/prize sprite and draws it on the background
*/
{
int x;
int y;
BOARDCELL(cell_x, cell_y)->sprite = ix;
x = PIXELX(cell_x, 0);
y = PIXELY(cell_y, 0);
add_background(x, y, CELL_WIDTH, CELL_HEIGHT);
XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
x, y, CELL_WIDTH, CELL_HEIGHT);
back_sprite(ix, 0, x, y);
return;
}
/*}}}*/
/*{{{ void show_updates()*/
extern VOIDFUNC show_updates FUNCARGVOID
/*
* shows all the updates on the update list
* and delete the dead apples and monsters.
* remember to keep the monster lists intact.
* the monsters a drawn in reverse order, so that the player
* ends up on top of everybody.
* We must be careful about the order of the update, so that there
* isn't excessive flicker. That's why we have a copy window to do the
* updates first, and then copy the relevant areas onto the display window
*/
{
draw_ball(0);
/*{{{ backgrounds to copy*/
{
unsigned i;
BACKGROUND *bptr;
for(bptr = update.back.list, i = update.back.backs; i--; bptr++)
XCopyArea(display.display, display.back, display.copy, GCN(GC_COPY),
bptr->place.x, bptr->place.y, bptr->size.x, bptr->size.y,
bptr->place.x, bptr->place.y);
}
/*}}}*/
if(global.state != MODE_ZOOM)
{
/*{{{ do the monster backgrounds*/
{
int i;
MONSTER *mptr;
MONSTER *nptr;
for(mptr = nptr = monster.list, i = monster.monsters; i--; mptr++)
{
int new;
update.set = 0;
if(mptr->type == 5)
new = 0;
else if(mptr->type == 6)
new = SPRITE_DIAMOND + mptr->image;
else if(mptr->type > 5)
new = mptr->type;
else if(mptr->chew)
new = SPRITE_CHOMP + mptr->image;
else if(mptr->face >= 16)
new = SPRITE_SQUISHED - 16 + mptr->type * 2 + mptr->face;
else
new = SPRITE_MONSTERS + mptr->type * (6 * MONSTER_IMAGES) +
mptr->face * MONSTER_IMAGES + mptr->image;
if(!mptr->back && mptr->old_sprite && (new != mptr->old_sprite ||
mptr->pixel.x != mptr->old_pixel.x ||
mptr->pixel.y != mptr->old_pixel.y ||
(mptr == &monster.list[0] &&
!player.old_ball.state && player.ball.state)) &&
mptr->on)
{
XCopyArea(display.display, display.back, display.copy,
GCN(GC_COPY),
mptr->old_pixel.x, mptr->old_pixel.y,
CELL_WIDTH, CELL_HEIGHT,
mptr->old_pixel.x, mptr->old_pixel.y);
if(mptr->pixel.x != mptr->old_pixel.x ||
mptr->pixel.y != mptr->old_pixel.y || !new)
bounding_box(mptr->old_pixel.x, mptr->old_pixel.y,
CELL_WIDTH, CELL_HEIGHT);
}
mptr->old_sprite = new;
if(new)
{
mptr->old_pixel.x = mptr->pixel.x;
mptr->old_pixel.y = mptr->pixel.y;
mptr->on = INRANGE(mptr->pixel.x,
1 - CELL_WIDTH, WINDOW_WIDTH) &&
INRANGE(mptr->pixel.y, 1 - CELL_HEIGHT, WINDOW_HEIGHT);
if(!mptr->back && mptr->on)
bounding_box(mptr->old_pixel.x, mptr->old_pixel.y,
CELL_WIDTH, CELL_HEIGHT);
mptr->tptr = nptr;
nptr++;
}
else
mptr->tptr = NULL;
mptr->back = 0;
if(update.set)
add_background(update.tl.x, update.tl.y,
(unsigned)(update.br.x - update.tl.x),
(unsigned)(update.br.y - update.tl.y));
}
}
/*}}}*/
/*{{{ do the apple backgrounds*/
{
int i;
APPLE *aptr;
APPLE *dptr;
for(aptr = dptr = apple.list, i = apple.apples; i--; aptr++)
{
int new;
update.set = 0;
new = aptr->state;
if(!aptr->back && (new != aptr->old_state ||
aptr->pixel.x != aptr->old_pixel.x ||
aptr->pixel.y != aptr->old_pixel.y))
{
APPLE_SIZE CONST *asp;
int x, y;
unsigned width, height;
asp = &apple_sizes[aptr->old_state];
x = aptr->old_pixel.x + asp->offset.x;
y = aptr->old_pixel.y + asp->offset.y;
width = asp->size.x;
height = asp->size.y;
XCopyArea(display.display, display.back, display.copy,
GCN(GC_COPY), x, y, width, height, x, y);
bounding_box(x, y, width, height);
}
if(new != 6)
{
APPLE_SIZE CONST *asp;
aptr->old_pixel.x = aptr->pixel.x;
aptr->old_pixel.y = aptr->pixel.y;
aptr->old_state = new;
asp = &apple_sizes[new];
if(!aptr->back)
bounding_box(aptr->old_pixel.x + asp->offset.x,
aptr->old_pixel.y + asp->offset.y,
asp->size.x, asp->size.y);
if(aptr->list)
{
assert(aptr->list->tptr);
aptr->list = aptr->list->tptr;
}
aptr->back = 0;
if(aptr != dptr)
memcpy(dptr, aptr, sizeof(APPLE));
dptr++;
}
else
apple.apples--;
if(update.set)
add_background(update.tl.x, update.tl.y,
(unsigned)(update.br.x - update.tl.x),
(unsigned)(update.br.y - update.tl.y));
}
}
/*}}}*/
/*{{{ do the apple sprites*/
{
int i;
APPLE *aptr;
for(aptr = apple.list, i = apple.apples; i--; aptr++)
{
SPRITE *sptr;
APPLE_SIZE CONST *asp;
int x, y;
unsigned width, height;
asp = &apple_sizes[aptr->old_state];
sptr = &sprites[(aptr->ghost && aptr->old_state < 3 ?
SPRITE_GHOST : SPRITE_APPLE) + aptr->old_state];
x = aptr->old_pixel.x + asp->offset.x;
y = aptr->old_pixel.y + asp->offset.y;
width = asp->size.x;
height = asp->size.y;
XCopyArea(display.display, sptr->mask, display.copy, GCN(GC_MASK),
0, 0, width, height, x, y);
XCopyArea(display.display, sptr->image, display.copy, GCN(GC_OR),
0, 0, width, height, x, y);
}
}
/*}}}*/
/*{{{ do the monster sprites*/
{
int i;
MONSTER *mptr;
for(mptr = &monster.list[monster.monsters - 1], i = monster.monsters;
i--; mptr--)
if(mptr->old_sprite)
{
if(mptr->on)
{
SPRITE *sptr;
sptr = &sprites[mptr->old_sprite];
XCopyArea(display.display, sptr->mask, display.copy,
GCN(GC_MASK), 0, 0, CELL_WIDTH, CELL_HEIGHT,
mptr->old_pixel.x, mptr->old_pixel.y);
if(mptr->ghosting)
/*{{{ ghosting*/
{
SPRITE *dptr;
COORD offset;
dptr = &sprites[SPRITE_GHOSTING];
offset.x = mptr->ghosting & 0xF;
offset.y = (mptr->ghosting >> 4) & 0xF;
XCopyArea(display.display, sptr->image, dptr->image,
GCN(GC_COPY), 0, 0, CELL_WIDTH, CELL_HEIGHT, 0, 0);
XCopyArea(display.display, dptr->mask, dptr->image,
GCN(GC_AND), offset.x, offset.y,
CELL_WIDTH, CELL_HEIGHT, 0, 0);
XCopyArea(display.display, dptr->image, display.copy,
GCN(GC_OR), 0, 0, CELL_WIDTH, CELL_HEIGHT,
mptr->old_pixel.x, mptr->old_pixel.y);
XFillRectangle(display.display, dptr->image,
GCN(GC_CLEAR), 0, 0, CELL_WIDTH, CELL_HEIGHT);
XCopyArea(display.display, dptr->mask, dptr->image,
GCN(GC_MASK), offset.x, offset.y,
CELL_WIDTH, CELL_HEIGHT, 0, 0);
XCopyArea(display.display, sptr->mask, dptr->image,
GCN(GC_AND), 0, 0, CELL_WIDTH, CELL_HEIGHT, 0, 0);
sptr = dptr;
if(!mptr->cycle)
/*{{{ drift*/
{
unsigned temp;
temp = chaotic();
if(!(temp & 0x3) || offset.x == (mptr->ghosting &
0x200 ? GHOSTING_WIDTH - 1 - CELL_WIDTH : 0))
mptr->ghosting ^= 0x200;
if(!(temp & 0xC) || offset.y == (mptr->ghosting &
0x400 ? GHOSTING_HEIGHT - 1 - CELL_HEIGHT : 0))
mptr->ghosting ^= 0x400;
offset.x += mptr->ghosting & 0x200 ? 1 : -1;
offset.y += mptr->ghosting & 0x400 ? 1 : -1;
mptr->ghosting = (mptr->ghosting & 0x700) |
(offset.y << 4) | offset.x;
}
/*}}}*/
}
/*}}}*/
XCopyArea(display.display, sptr->image, display.copy,
GCN(GC_OR), 0, 0, CELL_WIDTH, CELL_HEIGHT,
mptr->old_pixel.x, mptr->old_pixel.y);
}
if(mptr->squished)
{
if(mptr->list)
{
assert(mptr->list->tptr ||
(mptr->type == 4 && mptr->list->type == 5));
mptr->list = mptr->list->tptr;
}
}
else
mptr->list = NULL;
}
}
/*}}}*/
}
memcpy(&player.old_ball, &player.ball, sizeof(BALL));
draw_ball(1);
/*{{{ scores to copy*/
{
unsigned i;
SCORE *sptr;
for(sptr = update.score.list, i = update.score.scores; i--; sptr++)
{
XCopyArea(display.display, sptr->mask, display.copy, GCN(GC_MASK),
0, 0, DIGIT_WIDTH * 4, DIGIT_HEIGHT,
sptr->place.x, sptr->place.y);
XCopyArea(display.display, sptr->image, display.copy, GCN(GC_OR),
0, 0, DIGIT_WIDTH * 4, DIGIT_HEIGHT,
sptr->place.x, sptr->place.y);
}
}
/*}}}*/
/*{{{ areas to window*/
{
unsigned i;
BACKGROUND *bptr;
for(bptr = update.back.list, i = update.back.backs; i--; bptr++)
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
bptr->place.x, bptr->place.y, bptr->size.x, bptr->size.y,
bptr->place.x, bptr->place.y);
}
/*}}}*/
/*{{{ scores to window*/
{
unsigned i;
SCORE *sptr;
for(sptr = update.score.list, i = update.score.scores; i--; sptr++)
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
sptr->place.x, sptr->place.y, DIGIT_WIDTH * 4, DIGIT_HEIGHT,
sptr->place.x, sptr->place.y);
}
/*}}}*/
/*{{{ delete monsters*/
{
MONSTER *mptr;
unsigned count;
for(mptr = monster.list, count = monster.monsters; count--; mptr++)
if(!mptr->old_sprite)
monster.monsters--;
else if(mptr != mptr->tptr)
memcpy(mptr->tptr, mptr, sizeof(MONSTER));
}
/*}}}*/
update.back.backs = 0;
XSync(display.display, False);
return;
}
/*}}}*/
/************************************************************************/
/* */
/* Munch the "partial" cells. */
/* This is pretty hackish, but it seems to work. */
/* */
/************************************************************************/
static INLINE void MunchStuff(void)
{
MONSTER Monster;
unsigned apples;
Monster.type=4;
Monster.pause=FALSE;
Monster.stop=FALSE;
Monster.panic=FALSE;
Monster.shot=FALSE;
Monster.fast=FALSE;
Monster.list=NULL;
Monster.tptr=NULL;
Monster.face=0;
Monster.push=FALSE;
Monster.gomunch=FALSE;
Monster.ghosting=FALSE;
Monster.cont=FALSE;
Monster.chew=FALSE;
Monster.count=0;
Monster.cycle=FALSE;
Monster.image=SPRITE_PLAYER;
Monster.pushing=FALSE;
Monster.squished=FALSE;
Monster.old_pixel.x=0;
Monster.old_pixel.y=0;
Monster.old_sprite=SPRITE_PLAYER;
Monster.back=0;
Monster.on=TRUE;
apples=apple.apples;
apple.apples=0;
for (Monster.cell.x=0; Monster.cell.x<CELLS_ACROSS; Monster.cell.x++)
{
for (Monster.cell.y=0; Monster.cell.y<CELLS_DOWN; Monster.cell.y++)
{
CELL *cptr;
int depths[4];
cptr=BOARDCELL(Monster.cell.x,Monster.cell.y);
if (cptr->visit)
{
memcpy(depths,cptr->depths,sizeof(cptr->depths));
cptr->depths[0]=0;
cptr->depths[1]=0;
cptr->depths[2]=0;
cptr->depths[3]=0;
for (Monster.dir=0; Monster.dir<4; Monster.dir++)
{
Monster.offset.x=0;
Monster.offset.y=0;
Monster.pixel.x=PIXELX(Monster.cell.x,Monster.offset.x);
Monster.pixel.y=PIXELY(Monster.cell.y,Monster.offset.y);
if (Monster.dir&1)
{
while (cptr->depths[Monster.dir]<depths[Monster.dir])
{
update.back.backs=0;
move_muncher(&Monster);
}
}
else
{
while (cptr->depths[Monster.dir]>depths[Monster.dir])
{
update.back.backs=0;
move_muncher(&Monster);
}
}
}
memcpy(cptr->depths,depths,sizeof(cptr->depths));
}
}
}
apple.apples=apples;
}
/************************************************************************/
/* */
/* Recreate the offscreen bitmaps. */
/* This is called after the window is opened and the sprites have been */
/* created. */
/* */
/************************************************************************/
void RedrawGame(void)
{
int cell_y;
create_xtra_monster(extra.select);
draw_blank_background(&board.list[(global.screen-1)%board.boards], (global.screen-1)/board.boards);
MunchStuff();
new_board_draw();
for (cell_y=0; cell_y!=CELLS_DOWN; cell_y++)
{
int cell_x;
for (cell_x=0; cell_x!=CELLS_ACROSS; cell_x++)
{
if(BOARDCELL(cell_x, cell_y)->den)
{
set_back_sprite(global.state ? global.state != 1 ?
0 : (global.screen - 1) % SPRITE_PRIZES +
SPRITE_PRIZE_BASE : SPRITE_DEN, cell_x, cell_y);
}
}
}
refresh_window();
}